#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <sys/types.h>
#include <linux/inotify.h>
#include "minotify.h"
#include "commons.h"
#include "log_util.h"

#define EVENT_SIZE  ( sizeof (struct inotify_event) )
#define EVENT_BUF_LEN     ( 1024 * ( EVENT_SIZE + 16 ) )

#define ACTION_CREATE	"CREATE"
#define ACTION_DELETE	"DELETE"

#define TARGET_FOLDER	"FOLDER"
#define TARGET_FILE	"FILE"

int g_wd = -1; /* global var, watch handler id */
int g_fd = -1; /* global var, inotify handler id */

#define DEBUG 1

/*
 * Main entry of this tool
 */
int main ( int argc, char **argv )
{
    if ( argc <=2)
    {
        printf ( "invalid parameter.\n" );
        return EXIT_FAILURE;
    }

    signal( SIGINT, signal_int_handler);

    foobar();

    watch_dir ( argv[1], argv[2] );

    exit ( EXIT_SUCCESS );
}

/*
 * signal handler, rememer to release resources in this function.
 */
void signal_int_handler( int signal __attribute__((unused)) ) {
    cleanup();

    fprintf( stdout, "\nTerminated by user\n");

    exit(EXIT_SUCCESS);
}

/*
 * clean up allocated resources.
 */
void cleanup()
{
    /* removing the target directory from th watch list. */
    debug_log("remove watch");
    inotify_rm_watch ( g_fd, g_wd );

    /* closing the INOTIFY instance */
    debug_log("close inotify instance");
    close ( g_fd );

    debug_log("close log file hanlder");
    close_log();
}

int watch_dir ( char *target, char *script)
{
    /* creating the INOTIFY instance */
    g_fd = inotify_init();

    /* checking for error */
    if ( g_fd < 0 )
    {
        perror ( "inotify_init" );
    }

    /* adding the target directory into watch list. */
    g_wd = inotify_add_watch ( g_fd, target, IN_ALL_EVENTS );

    /* read to determine the event change happens on target directory.
     * Actually this read blocks until the change event occurs */
    size_t length = -1;
    char buffer[EVENT_BUF_LEN] = {0};
    length = read ( g_fd, buffer, EVENT_BUF_LEN );

    while ( length )
    {
        /* checking for error */
        if ( length < 0 )
        {
            perror ( "read" );
        }

        /* actually read return the list of change events happens.
        * Here, read the change event one by one and process it accordingly. */
        size_t i = 0;
        while ( i < length )
        {
            struct inotify_event *event = ( struct inotify_event * ) &buffer[ i ];
            if ( event->len )
            {
#ifdef DEBUG
                printf("EVENT NAME:%s, EVENT MASK:%d\n", event->name, event->mask);
#endif
                if ( event->mask & IN_CREATE )
                {
                    if ( event->mask & IN_ISDIR )
                    {
                        call_external ( script, event->name, ACTION_CREATE, TARGET_FOLDER );
                    }
                    else
                    {
                        fork_external ( script, event->name, ACTION_CREATE, TARGET_FILE );
                    }
                }
                else if ( event->mask & IN_DELETE )
                {
                    if ( event->mask & IN_ISDIR )
                    {
                        call_external ( script, event->name, ACTION_DELETE, TARGET_FOLDER );
                    }
                    else
                    {
                        fork_external ( script, event->name, ACTION_DELETE, TARGET_FILE );
                    }
                }

                /*
                * TO BE ADDED, MORE EVENT HANDLER
                */
            }

            i += EVENT_SIZE + event->len;
        }

        memset(&buffer[0], 0, sizeof(buffer));
        length = read ( g_fd, buffer, EVENT_BUF_LEN );
    }

    cleanup();
}


int call_external (char *script, char *filename , const char *action_type, const char *target_type)
{
    int len = strlen(script) + strlen ( filename ) + strlen(action_type) + strlen(target_type) + 4;
    char *param = malloc(len);
    memset(param, 0, len);
    snprintf(param, len, "%s %s %s %s", script, filename, action_type, target_type);

    debug_log("filename:%s, action:%s, target: %s", filename, action_type, target_type);
    debug_log("param:%s", param);

    system (param);
    FREE(param);
}

int fork_external(char *script, char *filename, const char *action_type, const char *target_type)
{
    pid_t pid = fork();

    if (pid == 0)
    {
        execlp(script, script, filename, action_type, target_type, (char *)0);
    }
    else
    {
        // NOOP
    }
}
